/*
 * Epoll.cc
 *
 *  Created on: 2021年4月5日
 *      Author: san
 */

#include "Epoll.h"
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <deque>
#include <queue>
#include "Util.h"
#include "base/Logging.h"

#include <arpa/inet.h>
#include <iostream>

namespace san {

const int EVENTSNUM = 4096;
const int EPOLLWAIT_TIME = 10000;

Epoll::Epoll()
	: epollFd_(epoll_create1(EPOLL_CLOEXEC)),
	  events_(EVENTSNUM)
{
	assert(epollFd_ > 0);
}

Epoll::~Epoll()
{
}

// 注册新描述符
void Epoll::epoll_add(ChannelPtr request, int timeout)
{
	int fd = request->getFd();
	if (timeout > 0)
	{
		add_timer(request, timeout);
		fd2http_[fd] = request->getHolder();
	}

	struct epoll_event event;
	event.data.fd = fd;
	event.events = request->getEvents();

	request->EqualAndUpdateLastEvents();

	fd2chan_[fd] = request;

	// 向epollFd_ 注册描述符
	// FIXME: 这里为什么要用引用
	if (epoll_ctl(epollFd_, EPOLL_CTL_ADD, fd, &event) < 0)
	{
		perror("epoll_add_error");
		fd2chan_[fd].reset();
	}
}

// 更新epoll已关注描述符状态
void Epoll::epoll_mod(ChannelPtr request, int timeout)
{
	// 注册定时器
	if (timeout > 0) add_timer(request, timeout);
	int fd = request->getFd();
	if (!request->EqualAndUpdateLastEvents())
	{
		struct epoll_event event;
		event.data.fd = fd;
		event.events = request->getEvents();
		if (epoll_ctl(epollFd_, EPOLL_CTL_MOD, fd, &event) < 0)
		{
			perror("epoll_mod error");
			fd2chan_[fd].reset();
		}
	}
}

// 从epoll关注列表中删除描述符
void Epoll::epoll_del(ChannelPtr request)
{
	int fd = request->getFd();
	struct epoll_event event;
	event.data.fd = fd;

	// ??? 为什么删除revents事件， revent 和 event 有什么区别
	event.events = request->getLastEvents();

	// event.events = 0;
	// request->EqualAndUpdateLastEvents()

	if (epoll_ctl(epollFd_, EPOLL_CTL_DEL, fd, &event) < 0)
	{
		perror("epoll_del error");
	}

	fd2chan_[fd].reset();
	fd2http_[fd].reset();
}

// poll()
std::vector<std::shared_ptr<Channel> > Epoll::poll()
{
	// FIXME: 是否需要使用 while true，我感觉不需要
	int numEvents = epoll_wait(epollFd_,
						       &*events_.begin(),
							   events_.size(),
							   EPOLLWAIT_TIME);

	if ( -1 == numEvents)
	{
		if (errno != EINTR)
		{
			perror("epoll wait error");
		}
	}

	std::vector<ChannelPtr> req_data = getEventsRequest(numEvents);
	if (req_data.size() > 0) return req_data;
	return std::vector<std::shared_ptr<Channel> >();
}

void Epoll::handleExpired() { timerManager_.handleExpiredEvent(); }

// 分发处理函数
std::vector<ChannelPtr> Epoll::getEventsRequest(int events_num)
{
	std::vector<ChannelPtr> req_data;
	for (int i = 0; i < events_num; ++i)
	{
		// 获取请求事件描述符
		int fd = events_[i].data.fd;
		// 获取相应的channel对象
		ChannelPtr cur_rep = fd2chan_[fd];

		if (cur_rep)
		{
			cur_rep->setRevents(events_[i].events);  // 设置返回事件
			cur_rep->setEvents(0);                   // 设置请求事件

			// FIXME: seperateTimer??
			// 加入线程池之前将Timer和request分离
			// cur_req->seperateTimer();

			req_data.push_back(cur_rep);
		}
		else
			LOG << "ChannelPtr is NULL";
	}

	return req_data;
}

void Epoll::add_timer(ChannelPtr request_data, int timeout)
{
	std::shared_ptr<HttpData> t = request_data->getHolder();
	if (t)
		timerManager_.addTimer(t, timeout);
	else
		LOG << "timer add fail";
}

} /* namespace san */
